﻿
namespace Octopus.RemoteHpcPS

(*
#r "System.Management.Automation"

#quit
*)

open System.Management.Automation
open System.Management.Automation.Host
open System.Management.Automation.Runspaces
open System.IO



type private MyAuthorizationManager (shellId:string) =
  inherit AuthorizationManager(shellId)
  
  override x.ShouldRun (commandInfo:CommandInfo, origin:CommandOrigin, host:PSHost, reason:byref<System.Exception>) =
    reason <- null
    true



type private HpcPowerShell () =
  let ps = PowerShell.Create()
  let psInvoke() =
    try
      let res = ps.Invoke()
      if ps.Streams.Error.Count > 0 then
        let e = ps.Streams.Error.[0].Exception
        ps.Streams.Error.Clear()
        raise e
      res
    finally
      ps.Commands.Clear()

  do
    let t = typeof<RunspaceConfiguration>
    let f = t.GetField("_authorizationManager", System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.NonPublic)
    f.SetValue(ps.Runspace.RunspaceConfiguration, new MyAuthorizationManager("Microsoft.PowerShell"))
    
    let hpc2008R2Path = @"C:\Program Files\Microsoft HPC Pack 2008 R2\Bin"

    let myResolveEventHandler (sender:obj) (args:System.ResolveEventArgs) =
        let argName = if not(args.Name.Contains(",")) then args.Name
                      else args.Name.Substring(0,args.Name.IndexOf(","))
        let strTempAssmbPath = sprintf "%s\\%s.dll" hpc2008R2Path argName
        System.Reflection.Assembly.LoadFrom(strTempAssmbPath)
        
    System.AppDomain.CurrentDomain.add_AssemblyResolve(new System.ResolveEventHandler(myResolveEventHandler))
    
    ps.AddCommand("Add-PSSnapIn").AddArgument("Microsoft.Hpc") |> ignore
    ps.Invoke() |> ignore

    if ps.Streams.Error.Count > 0 then
      ps.Dispose()
      raise (ps.Streams.Error.[0].Exception)

    ps.Commands.Clear()
    ps.Streams.Error.Clear()
    
    

  member x.Dispose() = ps.Dispose()

  interface System.IDisposable with
    override x.Dispose() = x.Dispose()
      


  member x.GetNodeState (nodeName:string) =
    ps.AddCommand("Get-HpcNode") |> ignore
    ps.AddArgument(nodeName) |> ignore
    let res = psInvoke()
    res.[0].Properties.["NodeState"].Value.ToString()
    
  member x.SetNodeOnline (nodeName:string) =
    ps.AddCommand("Set-HpcNodeState") |> ignore
    ps.AddParameter("Name", nodeName) |> ignore
    ps.AddParameter("State", "Online") |> ignore
    psInvoke() |> ignore

  member x.SetNodeOffline (nodeName:string) =
    ps.AddCommand("Set-HpcNodeState") |> ignore
    ps.AddParameter("Name", nodeName) |> ignore
    ps.AddParameter("State", "Offline") |> ignore
    psInvoke() |> ignore

  member x.Assign_HpcNodeTemplate (nodeName:string) (templateName:string) =
    ps.AddCommand("Assign-HpcNodeTemplate") |> ignore
    ps.AddParameter("Name", templateName) |> ignore
    ps.AddParameter("NodeName", nodeName) |> ignore
    ps.AddParameter("Confirm", new SwitchParameter(false)) |> ignore
    psInvoke() |> ignore
 
  member x.Import_HpcNodeXml (xmlPath:string) =
    ps.AddCommand("Import-HpcNodeXml") |> ignore
    ps.AddParameter("Path", xmlPath) |> ignore
    ps.AddParameter("Confirm", new SwitchParameter(false)) |> ignore
    psInvoke() |> ignore



//-------------------------------------------------------


module internal Functions =

  let serviceBinPath = let s = typeof<HpcPsInstaller>.Assembly.Location in s.Substring(0, s.LastIndexOf(@"\"))

  let getNodeState nodeName =
      use ps = new HpcPowerShell()
      ps.GetNodeState nodeName

  let setNodeOnline nodeName =
      use ps = new HpcPowerShell()
      ps.SetNodeOnline nodeName

  let setNodeOffline nodeName =
      use ps = new HpcPowerShell()
      ps.SetNodeOffline nodeName

  let assign_HpcNodeTemplate nodeName templateName =
      use ps = new HpcPowerShell()
      ps.Assign_HpcNodeTemplate nodeName templateName
  
  let import_HpcNodeXml nodeName =
      let xmlPath = serviceBinPath + "\\XmlFiles\\" + nodeName + ".xml"
      if File.Exists(xmlPath) then
        use ps = new HpcPowerShell()
        ps.Import_HpcNodeXml xmlPath
      else
        failwith "Xml file not instantiated"
  
  //------------------
  
  let instanceHpcXmlNodeImport hostname domain (vmGuid:System.Guid) macAddress =
      let path = serviceBinPath + "\\XmlFiles"
      let xmlPath = path + "\\" + hostname + ".xml"
      if not(Directory.Exists(path)) then
        Directory.CreateDirectory(path) |> ignore
      let template =
          [| @"<?xml version=""1.0"" encoding=""utf-8""?>";
             @"<Nodes xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://schemas.microsoft.com/HpcNodeConfigurationFile/2007/12"">";
             @"  <Node";
             sprintf @"    Name=""%s""" hostname;
             sprintf @"    Domain=""%s""" domain;
             sprintf @"    MachineGuid=""{%s}"">" (vmGuid.ToString());
             @"    <Template Name=""Octopus ComputeNode Template"" />";
             sprintf @"    <MacAddress>%s</MacAddress>" macAddress;
             @"  </Node>";
             @"</Nodes>" |]
      use sw = new System.IO.StreamWriter(xmlPath, AutoFlush=true)
      for line in template do sw.WriteLine(line)
      sw.Close()


module internal RFunctions =

  let getNodeState (sr:StreamReader) (sw:StreamWriter) =
      let nodeName = sr.ReadLine()
      let ret = Functions.getNodeState nodeName
      sw.WriteLine(true)
      sw.WriteLine(ret)

  let setNodeOnline (sr:StreamReader) (sw:StreamWriter) =
      let nodeName = sr.ReadLine()
      Functions.setNodeOnline nodeName
      sw.WriteLine(true)

  let setNodeOffline (sr:StreamReader) (sw:StreamWriter) =
      let nodeName = sr.ReadLine()
      Functions.setNodeOffline nodeName
      sw.WriteLine(true)

  let assign_HpcNodeTemplate (sr:StreamReader) (sw:StreamWriter) =
      let nodeName = sr.ReadLine()
      let templateName = sr.ReadLine()
      Functions.assign_HpcNodeTemplate nodeName templateName
      sw.WriteLine(true)
  
  let import_HpcNodeXml (sr:StreamReader) (sw:StreamWriter) =
      let nodeName = sr.ReadLine()
      Functions.import_HpcNodeXml nodeName
      sw.WriteLine(true)
      
  //------
  
  let instanceHpcXmlNodeImport (sr:StreamReader) (sw:StreamWriter) =
      let hostname = sr.ReadLine()
      let domain = sr.ReadLine()
      let guid = new System.Guid( sr.ReadLine() )
      let macAddress = sr.ReadLine()
      
      Functions.instanceHpcXmlNodeImport hostname domain guid macAddress

      sw.WriteLine(true)     